Question 1

Describe a situation or problem from your job, everyday life, current events, etc., for which exponential smoothing would be appropriate. What data would you need? Would you expect the value of ?? (the first smoothing parameter) to be closer to 0 or 1, and why?

Response

Exponential smoothing can be a great optimization tool for a restaurant owner. They can use it to predict the amount of supplies they need and detect whether there are any trends in certain dishes. To determine the amount of supplies needed (ie pounds of flour or bottles of wine), they would need to gather historical consumption of the supply per day as the data needed and use a ?? value closer to 0. This is because restaurants can see large amounts of fluctuation due to local events. A football game at a nearby college may bring in a large number of hungry fans to celebrate a win.

Question 2

Using the 20 years of daily high temperature data for Atlanta (July through October) from Homework 2 Question 5, build and use an exponential smoothing model to help make a judgment of whether the unofficial end of summer has gotten later over the 20 years. (Part of the point of this assignment is for you to think about how you might use exponential smoothing to answer this question.) Note: in R, you can use either HoltWinters (simpler to use) or the smooth package’s es function (harder to use, but more general). If you use es, the Holt-Winters model uses model=“AAM” in the function call (the first and second constants are used “A”dditively, and the third (seasonality) is used “M”ultiplicatively; the documentation doesn’t make that clear).

Response

loading data and libraries

library(ggplot2)
library(reshape2)
library(qcc)
library(forecast)
library(plotly)
tempdf <- read.table('tempYearsCol.txt', header=TRUE)

Exponential decay of alpha

Before diving into the homework assignment, I wanted to get a better intuition of how quickly the weight decays in our exponential smoothing function with respect to alpha. This can tell me how much of the “older” data do we really take into account when we calculate the newest \(S_{t}\) value in the below exponential smoothing equation.

\[S_{t} = \alpha x_{t} + (1 + \alpha) \alpha x_{t-1} + (1 + \alpha)^2 \alpha x_{t-2} + (1 + \alpha)^3 \alpha x_{t-3} + ...\]

loop <- seq(.1,0.9,0.05)
ary=matrix(NA,11,length(loop))
for (j in 1:length(loop))
{
  for (i in 1:11)
  {
    a=loop[j]
    ary[i,j] <- a*(1-a)^(i-1)
  }
}
colnames(ary) <- loop
meltdf <-melt(ary,id="loop")
meltdf$Var2=as.factor(meltdf$Var2)
colnames(meltdf) <- c("Var1",  "a",  "value")
ggplot(meltdf,aes(x=Var1,y=value,color=a,group=a)) + geom_line() + labs(title="Exp Smoothing Decay Across Time Periods", x="Number of Time Periods Back", y="variable weight")+theme_minimal()

From this graph, you can really see that with a low alpha value, the weight given to the latest data point is very low but it only gradually decays from there, taking into account all of the previous time periods. With a high alpha value, we give the latest data point a very high weight and then quickly decay it down to make previous data points almost negligable. It seems that 3 time periods back is the elbow joint for many of the high value alpha points.

HoltWinters Model

My method of applying Holtwinters model to determine whether the unofficial end of summer has gotten later over the 20 years is to first apply cusum to the dataset to determine the dates of the unofficial end of summer, then apply HoltWinters model to determine a trend value to see whether it is getting later or not.

EosIndAry <- vector()
years <- colnames(tempdf)[2:length(colnames(tempdf))]
#loop through every year to generate an array of unofficial end of summer using CUSUM
for (i in 1:length(years))
{
  cusumAry <- cusum(tempdf[i+1],plot=FALSE)$violations$lower
  EosIndAry[i] <- min(cusumAry[cusumAry>63]) #any end of summer dates count as false positive before August 31
}

Alpha value

One way to determine the best alpha value without going through optimization is to plot the model’s sum of squared errors against varying alpha values. Here it looks like the most optimal alpha value would be 0.2.

EosTS <- as.ts(EosIndAry)
errMat <- matrix(NA,9,2)
for (i in 1:9)
{
  #test alpha values of 0.1 to 0.9
  a=i/10
  errMat[i,2] <- HoltWinters(EosTS,alpha=a,beta=TRUE,gamma=FALSE)$SSE
  errMat[i,1] <- a
}
colnames(errMat) <- c("a","Err")
ggplot(data.frame(errMat),aes(x=a,y=Err))+geom_point()+labs(title="Error w.r.t. alpha value")

But being lazy, we can just have the algorithm calculate it for us.

Unofficial end of summer trend

By not placing an alpha value in the model, it will be optimally calculated and we can see it was pretty close to 0.2. We see the resulting trend

#Run HoltWinters model and look at trend
hwModel <- HoltWinters(EosTS,beta=TRUE,gamma=FALSE)
print(paste0("alpha value is: ",hwModel$alpha))
[1] "alpha value is: 0.198381683678516"
print(hwModel$fitted)
Time Series:
Start = 3 
End = 20 
Frequency = 1 
       xhat    level        trend
 3 91.00000 87.00000  4.000000000
 4 95.39676 91.19838  4.198381684
 5 94.27980 92.73909  1.540709211
 6 85.39365 89.06637 -3.672721781
 7 82.35827 85.71232 -3.354050834
 8 80.84589 83.27910 -2.433216115
 9 73.71266 78.49588 -4.783222934
10 70.63049 74.56319 -3.932692472
11 77.95378 76.25848  1.695297837
12 81.25447 78.75648  2.497993290
13 82.46121 80.60884  1.852365819
14 82.94029 81.77457  1.165725246
15 88.49411 85.13434  3.359768304
16 92.05459 88.59447  3.460128495
17 85.97074 87.28260 -1.311862411
18 87.44783 87.36522  0.082613838
19 87.35276 87.35899 -0.006227632
20 87.20657 87.28278 -0.076209135

At the current year, the appearance is that the unofficial end of summer is actually getting earlier by a very small amount. To answer the question, the end of summer has not gotten later over the 20 years.

dateInd <- round(hwModel$fitted[,1])
datesVal <- tempdf$DAY[dateInd]
datesVal <- as.Date(datesVal,format = "%d-%B")
yearVal <- colnames(tempdf)[4:21]
yearVal <- as.integer(gsub('X','',yearVal))
qplot(yearVal,datesVal)+labs(title="Date of End of Summer for each year",y="Date",x="Year")+scale_y_date()

Response (Alternate)

HoltWinters before cusum

The above method does not seem to smooth out the data before we actually check for trend so let’s actually use HoltzWinters before we apply cusum to each year so that we smooth out the data from year to year to see if there’s actually any trend there.

#elongate the matrix into a single vector
vec <- as.vector(as.matrix(tempdf[,2:21]))
#turn the vector into a time series object with a frequency of 123 which is the number of samples we have per year
vects <- ts(vec,frequency=123)
#This can be automatically calculated, but I wanted to smooth out the model more rather than have the model default to a value of 0.9
alphaVal <- 0.81
#run this on various holtwinters models
hwModel <- HoltWinters(vec,alpha=alphaVal,beta=FALSE,gamma=FALSE)
hwTrend <-HoltWinters(vec,alpha=alphaVal,beta=TRUE,gamma=FALSE)
hwTrendSeasAdd <- HoltWinters(vects,alpha=alphaVal,beta=TRUE,gamma=TRUE,seasonal="additive")
hwTrendSeasMult <- HoltWinters(vects,alpha=alphaVal,beta=TRUE,gamma=TRUE,seasonal="multiplicative")

Understanding differences between the model

Below, we can see a breakdown of how each individual level contributed to each method.

plot(fitted(hwModel))

plot(fitted(hwTrend))

plot(fitted(hwTrendSeasAdd))

plot(fitted(hwTrendSeasMult))

length(hwModel$fitted[,1])
[1] 2459
length(hwTrend$fitted[,1])
[1] 2458
length(hwTrendSeasAdd$fitted[,1])
[1] 2337
length(hwTrendSeasMult$fitted[,1])
[1] 2337

Above counts is a good display of how each method words * hwModel ran it only with alpha value so this method has 2459 data points, one less than our original dataset * hwTrend ran with alpha and beta values to detect trend. To have trend, we’ll need to subtract the previous level so this will only have 2458 data points. * both seasonality method needs to subtract the last season which has a frequency of 123 so this would be 2337.

Is summer ending later?

Now we can dig into when summer is actually ending after we’ve smoothed out the time data based on the four different models.

NormAry <- vector()
TrendAry <- vector()
SeasAddAry <- vector()
SeasMultAry <- vector()
for (i in 1:19)
{
  
  start <- max((i-1)*123,1)
  end <- (i)*123
  Norm <- cusum(hwModel$fitted[start:end,1],plot=FALSE)$violations$lower
  Trend <- cusum(hwTrend$fitted[start:end,1],plot=FALSE)$violations$lower
  SeasAdd <- cusum(hwTrendSeasAdd$fitted[start:end,1],plot=FALSE)$violations$lower
  SeasMult <- cusum(hwTrendSeasMult$fitted[start:end,1],plot=FALSE)$violations$lower
  NormAry[i] <- min(Norm[Norm>63])
  TrendAry[i] <- min(Trend[Trend>63])
  SeasAddAry[i] <- min(SeasAdd[SeasAdd>63])
  SeasMultAry[i] <- min(SeasMult[SeasMult>63])
}
#setup datatype
NormDate <- tempdf$DAY[NormAry]
NormDate <- as.Date(NormDate,format = "%d-%B")
NormDate <- format(NormDate,format="%m/%d")
#collapse all the functions together
TrendDate <- format(as.Date(tempdf$DAY[TrendAry],format = "%d-%B"),format="%m/%d")
SeasAddDate <- format(as.Date(tempdf$DAY[SeasAddAry],format = "%d-%B"),format="%m/%d")
SeasMultDate <- format(as.Date(tempdf$DAY[SeasMultAry],format = "%d-%B"),format="%m/%d")
#get the x axis
yearVal <- colnames(tempdf)[3:21]
yearVal <- as.integer(gsub('X','',yearVal))
#plot each and name them
p1 <- plot_ly(x=yearVal,y=NormDate,type='scatter',mode='lines',name='Smooth')
p2 <- plot_ly(x=yearVal,y=TrendDate,type='scatter',mode='lines',name='Trend')
p3 <- plot_ly(x=yearVal,y=SeasAddDate,type='scatter',mode='lines',name='SeasonAdd')
p4 <- plot_ly(x=yearVal,y=SeasMultDate,type='scatter',mode='lines',name='SeasonMult')
#collect in a subplot
subplot(p1,p2,p3,p4,nrows=4) %>% layout(title="HoltWinter + CUSUM to determine End of Summer Trend")

When looking at the above graph, we can see the data much better than it was before! We can see that if we include seasonality, we really do start seeing the trend as the last day of summer starts getting pushed later and later.

Question 3

Describe a situation or problem from your job, everyday life, current events, etc., for which a linear regression model would be appropriate. List some (up to 5) predictors that you might use.

Response

A local ice cream shop may be able to predict the number of popsicle sales based on the following predictors: * temperature of the day * number of events happening in town * number of positive reviews the ice cream shop had in the past week on yelp * number of advertisements posted around town and online * % discount offered on coupons that week

Question 4

Using crime data from http://www.statsci.org/data/general/uscrime.txt (description at http://www.statsci.org/data/general/uscrime.html ), use regression (a useful R function is lm or glm) to predict the observed crime rate in a city with the following data: M = 14.0 So = 0 Ed = 10.0 Po1 = 12.0 Po2 = 15.5 LF = 0.640 M.F = 94.0 Pop = 150 NW = 1.1 U1 = 0.120 U2 = 3.6 Wealth = 3200 Ineq = 20.1 Prob = 0.04 Time = 39.0 Show your model (factors used and their coefficients), the software output, and the quality of fit.

Response

Load data and library

library(ggplot2)
library(graphics)
library(tsne)
library(caret)
library(knitr)
#import crime data
crimeTbl <- read.csv('uscrime.csv', header=TRUE)
#crime data features
crimeFeat <- crimeTbl[,1:dim(crimeTbl)[2]-1]
#crime data labels
crimeLab <- crimeTbl[,dim(crimeTbl)[2]]

Linear Regression on all features

From here, we can run linear regression with Crime being the dependent variable and everything else as the independent variable. I decided to run it on the entire set of features for the first test. It returned the following coefficients for each variable.

model <- lm(Crime~.,crimeTbl)
coefMat <- data.frame(model$coefficients)
kable(format(coefMat, scientific=F))
model.coefficients
(Intercept) -5984.28760450
M 87.83017324
So -3.80345030
Ed 188.32431475
Po1 192.80433828
Po2 -109.42192538
LF -663.82614508
M.F 17.40685553
Pop -0.73300815
NW 4.20446100
U1 -5827.10272440
U2 167.79967222
Wealth 0.09616624
Ineq 70.67209945
Prob -4855.26581548
Time -3.47901784

Linear Regression Performance on all features

To assess the performance of the model, I used the \(R^2\) value along with a plot of the residuals to determine how well the model fits the data. First, we’ll look at the r.squared value and also the adj.r.squared value. Searching online for the difference between the two, it looks like the r.squared value is the standard r.squared value we see describe by the formula below. This \(R^2\) value will always increase with the number of variables you provide to the linear regression. adj.r.squared is actually a r.squared value that adjusts for that addition by taking into account the number of variables that gets added to the model.

\[R^2=1-\frac{SS_{err}}{SS_{tot}}\] \(SS_{err}\): Sum of squared distances between the actual and predicted Y values \(SS_{tot}\): Sum of squared distances between the actual Y values and their mean

modelSum <- summary(model)
print(paste0("r.squared value is: ",modelSum$r.squared))
[1] "r.squared value is: 0.803086758316909"
print(paste0("adj.r.squared value is: ",modelSum$adj.r.squared))
[1] "adj.r.squared value is: 0.70780615750251"

The difference between the two may explain overfitting if the difference is large. I think the difference is decently large so it is showing that we may be overfitting our data. Next, we’ll take a look at the graph of the residuals

qplot(seq_along(model$residuals),model$residuals)+geom_hline(yintercept=0,colour='red')+labs(title="Linear Regression Residual Plot",x="city",y="residual")

We can see a decent amount of variation in the datapoints some of which hitting up to 512 crimes when our max data value went up to 1993. There is a decent amount of variation so later on I’m going to see if I can narrow this down a bit more. But first, I used this model to predict the number of crimes based on the homework data provided to us.

x <- data.frame("int" = 1
,"M" = 14.0
,"So" = 0
,"Ed" = 10.0
,"Po1" = 12.0
,"Po2" = 15.5
,"LF" = 0.640
,"M.F" = 94.0
,"Pop" = 150
,"NW" = 1.1
,"U1" = 0.120
,"U2" = 3.6
,"Wealth" = 3200
,"Ineq" = 20.1
,"Prob" = 0.04
,"Time" = 39.0)
x <- as.matrix(x)
crime <- x %*% as.matrix(model$coefficients)
print(paste0("Number of crimes in this city is: ",crime))
[1] "Number of crimes in this city is: 155.434896887448"

Picking Features

One awesome thing about lm in r is that you can pull the t value and the P value directly from the model. I’m going to use these values to pick out my features.

tpvalue <- data.frame(modelSum$coefficients[,3:4])
colnames(tpvalue) <- c('tValue','pValue')
kable(tpvalue[order(tpvalue[,2]),])
tValue pValue
(Intercept) -3.6751336 0.0008930
Ineq 3.1110441 0.0039831
Ed 3.0331654 0.0048614
Prob -2.1366485 0.0406269
M 2.1055390 0.0434434
U2 2.0379878 0.0501613
Po1 1.8170288 0.0788920
U1 -1.3840149 0.1762380
Po2 -0.9314285 0.3588296
Wealth 0.9276542 0.3607538
M.F 0.8552122 0.3989953
NW 0.6487473 0.5212791
Pop -0.5684193 0.5738452
Time -0.4855386 0.6307084
LF -0.4516657 0.6546541
So -0.0255685 0.9797654

From the table above, you can see that the following values are good predictors because their p-value is 0.05 or smaller.

  • Ineq: Income inequality: percentage of families earning below half the median income
  • Ed: mean years of schooling of the population aged 25 years or over
  • Prob: probability of imprisonment: ratio of number of commitments to number of offenses
  • M: percentage of males aged 14-24 in total state population
  • U2: unemployment rate of urban males 35-39
  • Po1: per capita expenditure on police protection in 1960

Below is a list of variables that are bad predictors.

  • So: indicator variable for a southern state
  • LF: labour force participation rate of civilian urban males in the age-group 14-24
  • Time: average time in months served by offenders in state prisons before their first release
  • Pop: state population in 1960 in hundred thousands
  • NW: percentage of nonwhites in the population
  • M.F: number of males per 100 females
  • Wealth: wealth: median value of transferable assets or family income
  • Po2: per capita expenditure on police protection in 1959
  • U1:unemployment rate of urban males 14-24

Let’s also take a look at the correlation plot between all of the variable to find out which ones are highly correlated.

library(corrplot)
M <- cor(crimeTbl)
corrplot.mixed(M,title="Correlation of Features",mar=c(0,0,1,0))

From this figure, we can see the following features are highly correlated so we should probably only include one of them in the selected features. * Wealth :: Ineq * Ineq :: Ed * So :: Ed * Wealth :: Po1 :: Po2

Linear Regression on Selected Features

I actually tested this model by removing either Ineq and Ed and checked the adjusted r-squared value afound that removing either one significantly dropped the value. It seems that they may be helping the model in different ways.

featToUse <- c('Ineq','Ed','Prob','M','U2','Po1','Crime')
smallCrimeTbl <- crimeTbl[featToUse]
model2 <- lm(Crime~.,smallCrimeTbl)
model2Sum <- summary(model2)
print(paste0("r.squared value is: ",model2Sum$r.squared))
[1] "r.squared value is: 0.765866328518745"
print(paste0("adj.r.squared value is: ",model2Sum$adj.r.squared))
[1] "adj.r.squared value is: 0.730746277796556"

We’re not seeing as large of a difference as we saw before.

qplot(seq_along(model2Sum$residuals),model2Sum$residuals)+geom_hline(yintercept=0,colour='red')+labs(title="Linear Regression Residual Plot",x="city",y="residual")

Because the scales are slightly adjusted, I can’t say for sure that the spread has changed, so it would be inconclusive from this chart. The best way to determine the difference is to run cross validation on both models.

Cross Validation

We can perform cross validation on both models. First one where we used all the features and the second one where we only used a series of features we hand picked.

library(DAAG)
crimedf <- data.frame(crimeTbl)
modAll <- CVlm(crimedf,form.lm=formula(Crime~.),m=5,seed=44,printit=FALSE,plotit="Residual",main="All features")
prediction from a rank-deficient fit may be misleadingprediction from a rank-deficient fit may be misleadingprediction from a rank-deficient fit may be misleadingprediction from a rank-deficient fit may be misleadingprediction from a rank-deficient fit may be misleading


 As there is >1 explanatory variable, cross-validation
 predicted values for a fold are not a linear function
 of corresponding overall predicted values.  Lines that
 are shown for the different folds are approximate

modSel <- CVlm(smallCrimeTbl,form.lm=formula(Crime~.),m=5,seed=44,printit=FALSE,plotit="Residual",main="Select features")
prediction from a rank-deficient fit may be misleadingprediction from a rank-deficient fit may be misleadingprediction from a rank-deficient fit may be misleadingprediction from a rank-deficient fit may be misleadingprediction from a rank-deficient fit may be misleading


 As there is >1 explanatory variable, cross-validation
 predicted values for a fold are not a linear function
 of corresponding overall predicted values.  Lines that
 are shown for the different folds are approximate

From plotting out the residuals it looks like model2(select features) is more consistent and form a tighter grouping than model1(all features). So we go ahead and take a look at the \(R^2\) value and also the mean squared error of both plots.

SStot <- sum((modAll$Crime-mean(modAll$Crime))^2)
SSres_mod1 <- sum((modAll$Predicted-modAll$Crime)^2)
SSres_mod2 <- sum((modSel$Predicted-modSel$Crime)^2)
#Calculate R^2
mod1R2 <- 1-SSres_mod1/SStot
mod2R2 <- 1-SSres_mod2/SStot
print(paste0('all features R^2: ',mod1R2))
[1] "all features R^2: 0.803086758316909"
print(paste0('select features R^2: ',mod2R2))
[1] "select features R^2: 0.765866328518745"
print(paste0('all features mean square: ',attr(modAll,"ms")))
[1] "all features mean square: 234975.270850653"
print(paste0('select features mean square: ',attr(modSel,"ms")))
[1] "select features mean square: 367815.594652495"

When we look at the cross validation data, we can see that the model with all features actually does better than the selected features model. This actually comes as a surprise to me since the adjusted r squared value displayed better performance for the select features. But this is the reason why we perform cross validation to confirm.

LS0tDQp0aXRsZTogIlRpbWUgU2VyaWVzIGFuZCBSZWdyZXNzaW9uIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCi0tLQ0KDQojIFF1ZXN0aW9uIDENCkRlc2NyaWJlIGEgc2l0dWF0aW9uIG9yIHByb2JsZW0gZnJvbSB5b3VyIGpvYiwgZXZlcnlkYXkgbGlmZSwgY3VycmVudCBldmVudHMsIGV0Yy4sIGZvciB3aGljaCBleHBvbmVudGlhbA0Kc21vb3RoaW5nIHdvdWxkIGJlIGFwcHJvcHJpYXRlLiBXaGF0IGRhdGEgd291bGQgeW91IG5lZWQ/IFdvdWxkIHlvdSBleHBlY3QgdGhlIHZhbHVlIG9mID8/ICh0aGUNCmZpcnN0IHNtb290aGluZyBwYXJhbWV0ZXIpIHRvIGJlIGNsb3NlciB0byAwIG9yIDEsIGFuZCB3aHk/DQoNCiMjIFJlc3BvbnNlDQpFeHBvbmVudGlhbCBzbW9vdGhpbmcgY2FuIGJlIGEgZ3JlYXQgb3B0aW1pemF0aW9uIHRvb2wgZm9yIGEgcmVzdGF1cmFudCBvd25lci4gIFRoZXkgY2FuIHVzZSBpdCB0byBwcmVkaWN0IHRoZSBhbW91bnQgb2Ygc3VwcGxpZXMgdGhleSBuZWVkIGFuZCBkZXRlY3Qgd2hldGhlciB0aGVyZSBhcmUgYW55IHRyZW5kcyBpbiBjZXJ0YWluIGRpc2hlcy4gIFRvIGRldGVybWluZSB0aGUgYW1vdW50IG9mIHN1cHBsaWVzIG5lZWRlZCAoaWUgcG91bmRzIG9mIGZsb3VyIG9yIGJvdHRsZXMgb2Ygd2luZSksIHRoZXkgd291bGQgbmVlZCB0byBnYXRoZXIgaGlzdG9yaWNhbCBjb25zdW1wdGlvbiBvZiB0aGUgc3VwcGx5IHBlciBkYXkgYXMgdGhlIGRhdGEgbmVlZGVkIGFuZCB1c2UgYSA/PyB2YWx1ZSBjbG9zZXIgdG8gMC4gIFRoaXMgaXMgYmVjYXVzZSByZXN0YXVyYW50cyBjYW4gc2VlIGxhcmdlIGFtb3VudHMgb2YgZmx1Y3R1YXRpb24gZHVlIHRvIGxvY2FsIGV2ZW50cy4gIEEgZm9vdGJhbGwgZ2FtZSBhdCBhIG5lYXJieSBjb2xsZWdlIG1heSBicmluZyBpbiBhIGxhcmdlIG51bWJlciBvZiBodW5ncnkgZmFucyB0byBjZWxlYnJhdGUgYSB3aW4uICANCg0KIyBRdWVzdGlvbiAyDQpVc2luZyB0aGUgMjAgeWVhcnMgb2YgZGFpbHkgaGlnaCB0ZW1wZXJhdHVyZSBkYXRhIGZvciBBdGxhbnRhIChKdWx5IHRocm91Z2ggT2N0b2JlcikgZnJvbSBIb21ld29yayAyDQpRdWVzdGlvbiA1LCBidWlsZCBhbmQgdXNlIGFuIGV4cG9uZW50aWFsIHNtb290aGluZyBtb2RlbCB0byBoZWxwIG1ha2UgYSBqdWRnbWVudCBvZiB3aGV0aGVyIHRoZQ0KdW5vZmZpY2lhbCBlbmQgb2Ygc3VtbWVyIGhhcyBnb3R0ZW4gbGF0ZXIgb3ZlciB0aGUgMjAgeWVhcnMuIChQYXJ0IG9mIHRoZSBwb2ludCBvZiB0aGlzIGFzc2lnbm1lbnQgaXMgZm9yDQp5b3UgdG8gdGhpbmsgYWJvdXQgaG93IHlvdSBtaWdodCB1c2UgZXhwb25lbnRpYWwgc21vb3RoaW5nIHRvIGFuc3dlciB0aGlzIHF1ZXN0aW9uLikNCk5vdGU6IGluIFIsIHlvdSBjYW4gdXNlIGVpdGhlciBIb2x0V2ludGVycyAoc2ltcGxlciB0byB1c2UpIG9yIHRoZSBzbW9vdGggcGFja2FnZSdzIGVzIGZ1bmN0aW9uIChoYXJkZXINCnRvIHVzZSwgYnV0IG1vcmUgZ2VuZXJhbCkuIElmIHlvdSB1c2UgZXMsIHRoZSBIb2x0LVdpbnRlcnMgbW9kZWwgdXNlcyBtb2RlbD0iQUFNIiBpbiB0aGUgZnVuY3Rpb24gY2FsbA0KKHRoZSBmaXJzdCBhbmQgc2Vjb25kIGNvbnN0YW50cyBhcmUgdXNlZCAiQSJkZGl0aXZlbHksIGFuZCB0aGUgdGhpcmQgKHNlYXNvbmFsaXR5KSBpcyB1c2VkDQoiTSJ1bHRpcGxpY2F0aXZlbHk7IHRoZSBkb2N1bWVudGF0aW9uIGRvZXNuJ3QgbWFrZSB0aGF0IGNsZWFyKS4NCg0KIyMgUmVzcG9uc2UNCg0KIyMjIGxvYWRpbmcgZGF0YSBhbmQgbGlicmFyaWVzDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShyZXNoYXBlMikNCmxpYnJhcnkocWNjKQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkocGxvdGx5KQ0KDQp0ZW1wZGYgPC0gcmVhZC50YWJsZSgndGVtcFllYXJzQ29sLnR4dCcsIGhlYWRlcj1UUlVFKQ0KYGBgDQoNCiMjIyBFeHBvbmVudGlhbCBkZWNheSBvZiBhbHBoYQ0KQmVmb3JlIGRpdmluZyBpbnRvIHRoZSBob21ld29yayBhc3NpZ25tZW50LCBJIHdhbnRlZCB0byBnZXQgYSBiZXR0ZXIgaW50dWl0aW9uIG9mIGhvdyBxdWlja2x5IHRoZSB3ZWlnaHQgZGVjYXlzIGluIG91ciBleHBvbmVudGlhbCBzbW9vdGhpbmcgZnVuY3Rpb24gd2l0aCByZXNwZWN0IHRvIGFscGhhLiAgVGhpcyBjYW4gdGVsbCBtZSBob3cgbXVjaCBvZiB0aGUgIm9sZGVyIiBkYXRhIGRvIHdlIHJlYWxseSB0YWtlIGludG8gYWNjb3VudCB3aGVuIHdlIGNhbGN1bGF0ZSB0aGUgbmV3ZXN0ICRTX3t0fSQgdmFsdWUgaW4gdGhlIGJlbG93IGV4cG9uZW50aWFsIHNtb290aGluZyBlcXVhdGlvbi4NCg0KJCRTX3t0fSA9IFxhbHBoYSB4X3t0fSArICgxICsgXGFscGhhKSBcYWxwaGEgeF97dC0xfSArICgxICsgXGFscGhhKV4yIFxhbHBoYSB4X3t0LTJ9ICsgKDEgKyBcYWxwaGEpXjMgXGFscGhhIHhfe3QtM30gKyAuLi4kJA0KDQoNCmBgYHtyfQ0KbG9vcCA8LSBzZXEoLjEsMC45LDAuMDUpDQphcnk9bWF0cml4KE5BLDExLGxlbmd0aChsb29wKSkNCmZvciAoaiBpbiAxOmxlbmd0aChsb29wKSkNCnsNCiAgZm9yIChpIGluIDE6MTEpDQogIHsNCiAgICBhPWxvb3Bbal0NCiAgICBhcnlbaSxqXSA8LSBhKigxLWEpXihpLTEpDQogIH0NCn0NCg0KY29sbmFtZXMoYXJ5KSA8LSBsb29wDQptZWx0ZGYgPC1tZWx0KGFyeSxpZD0ibG9vcCIpDQptZWx0ZGYkVmFyMj1hcy5mYWN0b3IobWVsdGRmJFZhcjIpDQpjb2xuYW1lcyhtZWx0ZGYpIDwtIGMoIlZhcjEiLCAgImEiLCAgInZhbHVlIikNCmdncGxvdChtZWx0ZGYsYWVzKHg9VmFyMSx5PXZhbHVlLGNvbG9yPWEsZ3JvdXA9YSkpICsgZ2VvbV9saW5lKCkgKyBsYWJzKHRpdGxlPSJFeHAgU21vb3RoaW5nIERlY2F5IEFjcm9zcyBUaW1lIFBlcmlvZHMiLCB4PSJOdW1iZXIgb2YgVGltZSBQZXJpb2RzIEJhY2siLCB5PSJ2YXJpYWJsZSB3ZWlnaHQiKSt0aGVtZV9taW5pbWFsKCkNCmBgYA0KRnJvbSB0aGlzIGdyYXBoLCB5b3UgY2FuIHJlYWxseSBzZWUgdGhhdCB3aXRoIGEgbG93IGFscGhhIHZhbHVlLCB0aGUgd2VpZ2h0IGdpdmVuIHRvIHRoZSBsYXRlc3QgZGF0YSBwb2ludCBpcyB2ZXJ5IGxvdyBidXQgaXQgb25seSBncmFkdWFsbHkgZGVjYXlzIGZyb20gdGhlcmUsIHRha2luZyBpbnRvIGFjY291bnQgYWxsIG9mIHRoZSBwcmV2aW91cyB0aW1lIHBlcmlvZHMuICBXaXRoIGEgaGlnaCBhbHBoYSB2YWx1ZSwgd2UgZ2l2ZSB0aGUgbGF0ZXN0IGRhdGEgcG9pbnQgYSB2ZXJ5IGhpZ2ggd2VpZ2h0IGFuZCB0aGVuIHF1aWNrbHkgZGVjYXkgaXQgZG93biB0byBtYWtlIHByZXZpb3VzIGRhdGEgcG9pbnRzIGFsbW9zdCBuZWdsaWdhYmxlLiAgSXQgc2VlbXMgdGhhdCAzIHRpbWUgcGVyaW9kcyBiYWNrIGlzIHRoZSBlbGJvdyBqb2ludCBmb3IgbWFueSBvZiB0aGUgaGlnaCB2YWx1ZSBhbHBoYSBwb2ludHMuDQoNCiMjIyBIb2x0V2ludGVycyBNb2RlbA0KTXkgbWV0aG9kIG9mIGFwcGx5aW5nIEhvbHR3aW50ZXJzIG1vZGVsIHRvIGRldGVybWluZSB3aGV0aGVyIHRoZSB1bm9mZmljaWFsIGVuZCBvZiBzdW1tZXIgaGFzIGdvdHRlbiBsYXRlciBvdmVyIHRoZSAyMCB5ZWFycyBpcyB0byBmaXJzdCBhcHBseSBjdXN1bSB0byB0aGUgZGF0YXNldCB0byBkZXRlcm1pbmUgdGhlIGRhdGVzIG9mIHRoZSB1bm9mZmljaWFsIGVuZCBvZiBzdW1tZXIsIHRoZW4gYXBwbHkgSG9sdFdpbnRlcnMgbW9kZWwgdG8gZGV0ZXJtaW5lIGEgdHJlbmQgdmFsdWUgdG8gc2VlIHdoZXRoZXIgaXQgaXMgZ2V0dGluZyBsYXRlciBvciBub3QuDQoNCmBgYHtyfQ0KRW9zSW5kQXJ5IDwtIHZlY3RvcigpDQp5ZWFycyA8LSBjb2xuYW1lcyh0ZW1wZGYpWzI6bGVuZ3RoKGNvbG5hbWVzKHRlbXBkZikpXQ0KDQojbG9vcCB0aHJvdWdoIGV2ZXJ5IHllYXIgdG8gZ2VuZXJhdGUgYW4gYXJyYXkgb2YgdW5vZmZpY2lhbCBlbmQgb2Ygc3VtbWVyIHVzaW5nIENVU1VNDQpmb3IgKGkgaW4gMTpsZW5ndGgoeWVhcnMpKQ0Kew0KICBjdXN1bUFyeSA8LSBjdXN1bSh0ZW1wZGZbaSsxXSxwbG90PUZBTFNFKSR2aW9sYXRpb25zJGxvd2VyDQogIEVvc0luZEFyeVtpXSA8LSBtaW4oY3VzdW1BcnlbY3VzdW1Bcnk+NjNdKSAjYW55IGVuZCBvZiBzdW1tZXIgZGF0ZXMgY291bnQgYXMgZmFsc2UgcG9zaXRpdmUgYmVmb3JlIEF1Z3VzdCAzMQ0KfQ0KYGBgDQoNCiMjIyBBbHBoYSB2YWx1ZQ0KT25lIHdheSB0byBkZXRlcm1pbmUgdGhlIGJlc3QgYWxwaGEgdmFsdWUgd2l0aG91dCBnb2luZyB0aHJvdWdoIG9wdGltaXphdGlvbiBpcyB0byBwbG90IHRoZSBtb2RlbCdzIHN1bSBvZiBzcXVhcmVkIGVycm9ycyBhZ2FpbnN0IHZhcnlpbmcgYWxwaGEgdmFsdWVzLiAgSGVyZSBpdCBsb29rcyBsaWtlIHRoZSBtb3N0IG9wdGltYWwgYWxwaGEgdmFsdWUgd291bGQgYmUgMC4yLg0KDQpgYGB7cn0NCkVvc1RTIDwtIGFzLnRzKEVvc0luZEFyeSkNCmVyck1hdCA8LSBtYXRyaXgoTkEsOSwyKQ0KZm9yIChpIGluIDE6OSkNCnsNCiAgI3Rlc3QgYWxwaGEgdmFsdWVzIG9mIDAuMSB0byAwLjkNCiAgYT1pLzEwDQogIGVyck1hdFtpLDJdIDwtIEhvbHRXaW50ZXJzKEVvc1RTLGFscGhhPWEsYmV0YT1UUlVFLGdhbW1hPUZBTFNFKSRTU0UNCiAgZXJyTWF0W2ksMV0gPC0gYQ0KfQ0KY29sbmFtZXMoZXJyTWF0KSA8LSBjKCJhIiwiRXJyIikNCg0KZ2dwbG90KGRhdGEuZnJhbWUoZXJyTWF0KSxhZXMoeD1hLHk9RXJyKSkrZ2VvbV9wb2ludCgpK2xhYnModGl0bGU9IkVycm9yIHcuci50LiBhbHBoYSB2YWx1ZSIpDQpgYGANCkJ1dCBiZWluZyBsYXp5LCB3ZSBjYW4ganVzdCBoYXZlIHRoZSBhbGdvcml0aG0gY2FsY3VsYXRlIGl0IGZvciB1cy4NCg0KIyMjIFVub2ZmaWNpYWwgZW5kIG9mIHN1bW1lciB0cmVuZA0KQnkgbm90IHBsYWNpbmcgYW4gYWxwaGEgdmFsdWUgaW4gdGhlIG1vZGVsLCBpdCB3aWxsIGJlIG9wdGltYWxseSBjYWxjdWxhdGVkIGFuZCB3ZSBjYW4gc2VlIGl0IHdhcyBwcmV0dHkgY2xvc2UgdG8gMC4yLiAgV2Ugc2VlIHRoZSByZXN1bHRpbmcgdHJlbmQgDQpgYGB7cn0NCiNSdW4gSG9sdFdpbnRlcnMgbW9kZWwgYW5kIGxvb2sgYXQgdHJlbmQNCmh3TW9kZWwgPC0gSG9sdFdpbnRlcnMoRW9zVFMsYmV0YT1UUlVFLGdhbW1hPUZBTFNFKQ0KcHJpbnQocGFzdGUwKCJhbHBoYSB2YWx1ZSBpczogIixod01vZGVsJGFscGhhKSkNCnByaW50KGh3TW9kZWwkZml0dGVkKQ0KYGBgDQpBdCB0aGUgY3VycmVudCB5ZWFyLCB0aGUgYXBwZWFyYW5jZSBpcyB0aGF0IHRoZSB1bm9mZmljaWFsIGVuZCBvZiBzdW1tZXIgaXMgYWN0dWFsbHkgZ2V0dGluZyBlYXJsaWVyIGJ5IGEgdmVyeSBzbWFsbCBhbW91bnQuICBUbyBhbnN3ZXIgdGhlIHF1ZXN0aW9uLCB0aGUgZW5kIG9mIHN1bW1lciBoYXMgbm90IGdvdHRlbiBsYXRlciBvdmVyIHRoZSAyMCB5ZWFycy4NCmBgYHtyfQ0KZGF0ZUluZCA8LSByb3VuZChod01vZGVsJGZpdHRlZFssMV0pDQpkYXRlc1ZhbCA8LSB0ZW1wZGYkREFZW2RhdGVJbmRdDQpkYXRlc1ZhbCA8LSBhcy5EYXRlKGRhdGVzVmFsLGZvcm1hdCA9ICIlZC0lQiIpDQp5ZWFyVmFsIDwtIGNvbG5hbWVzKHRlbXBkZilbNDoyMV0NCnllYXJWYWwgPC0gYXMuaW50ZWdlcihnc3ViKCdYJywnJyx5ZWFyVmFsKSkNCnFwbG90KHllYXJWYWwsZGF0ZXNWYWwpK2xhYnModGl0bGU9IkRhdGUgb2YgRW5kIG9mIFN1bW1lciBmb3IgZWFjaCB5ZWFyIix5PSJEYXRlIix4PSJZZWFyIikrc2NhbGVfeV9kYXRlKCkNCmBgYA0KDQojIyBSZXNwb25zZSAoQWx0ZXJuYXRlKQ0KDQojIyMgSG9sdFdpbnRlcnMgYmVmb3JlIGN1c3VtDQpUaGUgYWJvdmUgbWV0aG9kIGRvZXMgbm90IHNlZW0gdG8gc21vb3RoIG91dCB0aGUgZGF0YSBiZWZvcmUgd2UgYWN0dWFsbHkgY2hlY2sgZm9yIHRyZW5kIHNvIGxldCdzIGFjdHVhbGx5IHVzZSBIb2x0eldpbnRlcnMgYmVmb3JlIHdlIGFwcGx5IGN1c3VtIHRvIGVhY2ggeWVhciBzbyB0aGF0IHdlIHNtb290aCBvdXQgdGhlIGRhdGEgZnJvbSB5ZWFyIHRvIHllYXIgdG8gc2VlIGlmIHRoZXJlJ3MgYWN0dWFsbHkgYW55IHRyZW5kIHRoZXJlLg0KYGBge3J9DQojZWxvbmdhdGUgdGhlIG1hdHJpeCBpbnRvIGEgc2luZ2xlIHZlY3Rvcg0KdmVjIDwtIGFzLnZlY3Rvcihhcy5tYXRyaXgodGVtcGRmWywyOjIxXSkpDQojdHVybiB0aGUgdmVjdG9yIGludG8gYSB0aW1lIHNlcmllcyBvYmplY3Qgd2l0aCBhIGZyZXF1ZW5jeSBvZiAxMjMgd2hpY2ggaXMgdGhlIG51bWJlciBvZiBzYW1wbGVzIHdlIGhhdmUgcGVyIHllYXINCnZlY3RzIDwtIHRzKHZlYyxmcmVxdWVuY3k9MTIzKQ0KDQojVGhpcyBjYW4gYmUgYXV0b21hdGljYWxseSBjYWxjdWxhdGVkLCBidXQgSSB3YW50ZWQgdG8gc21vb3RoIG91dCB0aGUgbW9kZWwgbW9yZSByYXRoZXIgdGhhbiBoYXZlIHRoZSBtb2RlbCBkZWZhdWx0IHRvIGEgdmFsdWUgb2YgMC45DQphbHBoYVZhbCA8LSAwLjgxDQoNCiNydW4gdGhpcyBvbiB2YXJpb3VzIGhvbHR3aW50ZXJzIG1vZGVscw0KaHdNb2RlbCA8LSBIb2x0V2ludGVycyh2ZWMsYWxwaGE9YWxwaGFWYWwsYmV0YT1GQUxTRSxnYW1tYT1GQUxTRSkNCmh3VHJlbmQgPC1Ib2x0V2ludGVycyh2ZWMsYWxwaGE9YWxwaGFWYWwsYmV0YT1UUlVFLGdhbW1hPUZBTFNFKQ0KaHdUcmVuZFNlYXNBZGQgPC0gSG9sdFdpbnRlcnModmVjdHMsYWxwaGE9YWxwaGFWYWwsYmV0YT1UUlVFLGdhbW1hPVRSVUUsc2Vhc29uYWw9ImFkZGl0aXZlIikNCmh3VHJlbmRTZWFzTXVsdCA8LSBIb2x0V2ludGVycyh2ZWN0cyxhbHBoYT1hbHBoYVZhbCxiZXRhPVRSVUUsZ2FtbWE9VFJVRSxzZWFzb25hbD0ibXVsdGlwbGljYXRpdmUiKQ0KYGBgDQoNCiMjIyBVbmRlcnN0YW5kaW5nIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIG1vZGVsDQpCZWxvdywgd2UgY2FuIHNlZSBhIGJyZWFrZG93biBvZiBob3cgZWFjaCBpbmRpdmlkdWFsIGxldmVsIGNvbnRyaWJ1dGVkIHRvIGVhY2ggbWV0aG9kLg0KYGBge3IsZmlnLndpZHRoID0gMTMsIGZpZy5oZWlnaHQgPSA0fQ0KcGxvdChmaXR0ZWQoaHdNb2RlbCkpDQpwbG90KGZpdHRlZChod1RyZW5kKSkNCnBsb3QoZml0dGVkKGh3VHJlbmRTZWFzQWRkKSkNCnBsb3QoZml0dGVkKGh3VHJlbmRTZWFzTXVsdCkpDQpgYGANCg0KYGBge3J9DQpsZW5ndGgoaHdNb2RlbCRmaXR0ZWRbLDFdKQ0KbGVuZ3RoKGh3VHJlbmQkZml0dGVkWywxXSkNCmxlbmd0aChod1RyZW5kU2Vhc0FkZCRmaXR0ZWRbLDFdKQ0KbGVuZ3RoKGh3VHJlbmRTZWFzTXVsdCRmaXR0ZWRbLDFdKQ0KYGBgDQpBYm92ZSBjb3VudHMgaXMgYSBnb29kIGRpc3BsYXkgb2YgaG93IGVhY2ggbWV0aG9kIHdvcmRzDQoqIGh3TW9kZWwgcmFuIGl0IG9ubHkgd2l0aCBhbHBoYSB2YWx1ZSBzbyB0aGlzIG1ldGhvZCBoYXMgMjQ1OSBkYXRhIHBvaW50cywgb25lIGxlc3MgdGhhbiBvdXIgb3JpZ2luYWwgZGF0YXNldA0KKiBod1RyZW5kIHJhbiB3aXRoIGFscGhhIGFuZCBiZXRhIHZhbHVlcyB0byBkZXRlY3QgdHJlbmQuICBUbyBoYXZlIHRyZW5kLCB3ZSdsbCBuZWVkIHRvIHN1YnRyYWN0IHRoZSBwcmV2aW91cyBsZXZlbCBzbyB0aGlzIHdpbGwgb25seSBoYXZlIDI0NTggZGF0YSBwb2ludHMuDQoqIGJvdGggc2Vhc29uYWxpdHkgbWV0aG9kIG5lZWRzIHRvIHN1YnRyYWN0IHRoZSBsYXN0IHNlYXNvbiB3aGljaCBoYXMgYSBmcmVxdWVuY3kgb2YgMTIzIHNvIHRoaXMgd291bGQgYmUgMjMzNy4NCg0KIyMjIElzIHN1bW1lciBlbmRpbmcgbGF0ZXI/DQpOb3cgd2UgY2FuIGRpZyBpbnRvIHdoZW4gc3VtbWVyIGlzIGFjdHVhbGx5IGVuZGluZyBhZnRlciB3ZSd2ZSBzbW9vdGhlZCBvdXQgdGhlIHRpbWUgZGF0YSBiYXNlZCBvbiB0aGUgZm91ciBkaWZmZXJlbnQgbW9kZWxzLg0KDQpgYGB7cn0NCk5vcm1BcnkgPC0gdmVjdG9yKCkNClRyZW5kQXJ5IDwtIHZlY3RvcigpDQpTZWFzQWRkQXJ5IDwtIHZlY3RvcigpDQpTZWFzTXVsdEFyeSA8LSB2ZWN0b3IoKQ0KZm9yIChpIGluIDE6MTkpDQp7DQogIA0KICBzdGFydCA8LSBtYXgoKGktMSkqMTIzLDEpDQogIGVuZCA8LSAoaSkqMTIzDQogIE5vcm0gPC0gY3VzdW0oaHdNb2RlbCRmaXR0ZWRbc3RhcnQ6ZW5kLDFdLHBsb3Q9RkFMU0UpJHZpb2xhdGlvbnMkbG93ZXINCiAgVHJlbmQgPC0gY3VzdW0oaHdUcmVuZCRmaXR0ZWRbc3RhcnQ6ZW5kLDFdLHBsb3Q9RkFMU0UpJHZpb2xhdGlvbnMkbG93ZXINCiAgU2Vhc0FkZCA8LSBjdXN1bShod1RyZW5kU2Vhc0FkZCRmaXR0ZWRbc3RhcnQ6ZW5kLDFdLHBsb3Q9RkFMU0UpJHZpb2xhdGlvbnMkbG93ZXINCiAgU2Vhc011bHQgPC0gY3VzdW0oaHdUcmVuZFNlYXNNdWx0JGZpdHRlZFtzdGFydDplbmQsMV0scGxvdD1GQUxTRSkkdmlvbGF0aW9ucyRsb3dlcg0KICBOb3JtQXJ5W2ldIDwtIG1pbihOb3JtW05vcm0+NjNdKQ0KICBUcmVuZEFyeVtpXSA8LSBtaW4oVHJlbmRbVHJlbmQ+NjNdKQ0KICBTZWFzQWRkQXJ5W2ldIDwtIG1pbihTZWFzQWRkW1NlYXNBZGQ+NjNdKQ0KICBTZWFzTXVsdEFyeVtpXSA8LSBtaW4oU2Vhc011bHRbU2Vhc011bHQ+NjNdKQ0KfQ0KYGBgDQoNCg0KYGBge3IsZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDd9DQojc2V0dXAgZGF0YXR5cGUNCk5vcm1EYXRlIDwtIHRlbXBkZiREQVlbTm9ybUFyeV0NCk5vcm1EYXRlIDwtIGFzLkRhdGUoTm9ybURhdGUsZm9ybWF0ID0gIiVkLSVCIikNCk5vcm1EYXRlIDwtIGZvcm1hdChOb3JtRGF0ZSxmb3JtYXQ9IiVtLyVkIikNCiNjb2xsYXBzZSBhbGwgdGhlIGZ1bmN0aW9ucyB0b2dldGhlcg0KVHJlbmREYXRlIDwtIGZvcm1hdChhcy5EYXRlKHRlbXBkZiREQVlbVHJlbmRBcnldLGZvcm1hdCA9ICIlZC0lQiIpLGZvcm1hdD0iJW0vJWQiKQ0KU2Vhc0FkZERhdGUgPC0gZm9ybWF0KGFzLkRhdGUodGVtcGRmJERBWVtTZWFzQWRkQXJ5XSxmb3JtYXQgPSAiJWQtJUIiKSxmb3JtYXQ9IiVtLyVkIikNClNlYXNNdWx0RGF0ZSA8LSBmb3JtYXQoYXMuRGF0ZSh0ZW1wZGYkREFZW1NlYXNNdWx0QXJ5XSxmb3JtYXQgPSAiJWQtJUIiKSxmb3JtYXQ9IiVtLyVkIikNCiNnZXQgdGhlIHggYXhpcw0KeWVhclZhbCA8LSBjb2xuYW1lcyh0ZW1wZGYpWzM6MjFdDQp5ZWFyVmFsIDwtIGFzLmludGVnZXIoZ3N1YignWCcsJycseWVhclZhbCkpDQojcGxvdCBlYWNoIGFuZCBuYW1lIHRoZW0NCnAxIDwtIHBsb3RfbHkoeD15ZWFyVmFsLHk9Tm9ybURhdGUsdHlwZT0nc2NhdHRlcicsbW9kZT0nbGluZXMnLG5hbWU9J1Ntb290aCcpDQpwMiA8LSBwbG90X2x5KHg9eWVhclZhbCx5PVRyZW5kRGF0ZSx0eXBlPSdzY2F0dGVyJyxtb2RlPSdsaW5lcycsbmFtZT0nVHJlbmQnKQ0KcDMgPC0gcGxvdF9seSh4PXllYXJWYWwseT1TZWFzQWRkRGF0ZSx0eXBlPSdzY2F0dGVyJyxtb2RlPSdsaW5lcycsbmFtZT0nU2Vhc29uQWRkJykNCnA0IDwtIHBsb3RfbHkoeD15ZWFyVmFsLHk9U2Vhc011bHREYXRlLHR5cGU9J3NjYXR0ZXInLG1vZGU9J2xpbmVzJyxuYW1lPSdTZWFzb25NdWx0JykNCiNjb2xsZWN0IGluIGEgc3VicGxvdA0Kc3VicGxvdChwMSxwMixwMyxwNCxucm93cz00KSAlPiUgbGF5b3V0KHRpdGxlPSJIb2x0V2ludGVyICsgQ1VTVU0gdG8gZGV0ZXJtaW5lIEVuZCBvZiBTdW1tZXIgVHJlbmQiKQ0KYGBgDQpXaGVuIGxvb2tpbmcgYXQgdGhlIGFib3ZlIGdyYXBoLCB3ZSBjYW4gc2VlIHRoZSBkYXRhIG11Y2ggYmV0dGVyIHRoYW4gaXQgd2FzIGJlZm9yZSEgIFdlIGNhbiBzZWUgdGhhdCBpZiB3ZSBpbmNsdWRlIHNlYXNvbmFsaXR5LCB3ZSByZWFsbHkgZG8gc3RhcnQgc2VlaW5nIHRoZSB0cmVuZCBhcyB0aGUgbGFzdCBkYXkgb2Ygc3VtbWVyIHN0YXJ0cyBnZXR0aW5nIHB1c2hlZCBsYXRlciBhbmQgbGF0ZXIuDQoNCg0KDQojIFF1ZXN0aW9uIDMNCkRlc2NyaWJlIGEgc2l0dWF0aW9uIG9yIHByb2JsZW0gZnJvbSB5b3VyIGpvYiwgZXZlcnlkYXkgbGlmZSwgY3VycmVudCBldmVudHMsIGV0Yy4sIGZvciB3aGljaCBhIGxpbmVhcg0KcmVncmVzc2lvbiBtb2RlbCB3b3VsZCBiZSBhcHByb3ByaWF0ZS4gTGlzdCBzb21lICh1cCB0byA1KSBwcmVkaWN0b3JzIHRoYXQgeW91IG1pZ2h0IHVzZS4NCg0KIyMgUmVzcG9uc2UNCkEgbG9jYWwgaWNlIGNyZWFtIHNob3AgbWF5IGJlIGFibGUgdG8gcHJlZGljdCB0aGUgbnVtYmVyIG9mIHBvcHNpY2xlIHNhbGVzIGJhc2VkIG9uIHRoZSBmb2xsb3dpbmcgcHJlZGljdG9yczoNCiogdGVtcGVyYXR1cmUgb2YgdGhlIGRheQ0KKiBudW1iZXIgb2YgZXZlbnRzIGhhcHBlbmluZyBpbiB0b3duDQoqIG51bWJlciBvZiBwb3NpdGl2ZSByZXZpZXdzIHRoZSBpY2UgY3JlYW0gc2hvcCBoYWQgaW4gdGhlIHBhc3Qgd2VlayBvbiB5ZWxwDQoqIG51bWJlciBvZiBhZHZlcnRpc2VtZW50cyBwb3N0ZWQgYXJvdW5kIHRvd24gYW5kIG9ubGluZQ0KKiAlIGRpc2NvdW50IG9mZmVyZWQgb24gY291cG9ucyB0aGF0IHdlZWsNCg0KIyBRdWVzdGlvbiA0DQpVc2luZyBjcmltZSBkYXRhIGZyb20gaHR0cDovL3d3dy5zdGF0c2NpLm9yZy9kYXRhL2dlbmVyYWwvdXNjcmltZS50eHQgKGRlc2NyaXB0aW9uIGF0DQpodHRwOi8vd3d3LnN0YXRzY2kub3JnL2RhdGEvZ2VuZXJhbC91c2NyaW1lLmh0bWwgKSwgdXNlIHJlZ3Jlc3Npb24gKGEgdXNlZnVsIFIgZnVuY3Rpb24gaXMgbG0gb3IgZ2xtKSB0bw0KcHJlZGljdCB0aGUgb2JzZXJ2ZWQgY3JpbWUgcmF0ZSBpbiBhIGNpdHkgd2l0aCB0aGUgZm9sbG93aW5nIGRhdGE6DQpNID0gMTQuMA0KU28gPSAwDQpFZCA9IDEwLjANClBvMSA9IDEyLjANClBvMiA9IDE1LjUNCkxGID0gMC42NDANCk0uRiA9IDk0LjANClBvcCA9IDE1MA0KTlcgPSAxLjENClUxID0gMC4xMjANClUyID0gMy42DQpXZWFsdGggPSAzMjAwDQpJbmVxID0gMjAuMQ0KUHJvYiA9IDAuMDQNClRpbWUgPSAzOS4wDQpTaG93IHlvdXIgbW9kZWwgKGZhY3RvcnMgdXNlZCBhbmQgdGhlaXIgY29lZmZpY2llbnRzKSwgdGhlIHNvZnR3YXJlIG91dHB1dCwgYW5kIHRoZSBxdWFsaXR5IG9mIGZpdC4NCg0KIyMgUmVzcG9uc2UNCg0KIyMjIExvYWQgZGF0YSBhbmQgbGlicmFyeQ0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdyYXBoaWNzKQ0KbGlicmFyeSh0c25lKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoa25pdHIpDQoNCiNpbXBvcnQgY3JpbWUgZGF0YQ0KY3JpbWVUYmwgPC0gcmVhZC5jc3YoJ3VzY3JpbWUuY3N2JywgaGVhZGVyPVRSVUUpDQojY3JpbWUgZGF0YSBmZWF0dXJlcw0KY3JpbWVGZWF0IDwtIGNyaW1lVGJsWywxOmRpbShjcmltZVRibClbMl0tMV0NCiNjcmltZSBkYXRhIGxhYmVscw0KY3JpbWVMYWIgPC0gY3JpbWVUYmxbLGRpbShjcmltZVRibClbMl1dDQoNCmBgYA0KDQojIyMgTGluZWFyIFJlZ3Jlc3Npb24gb24gYWxsIGZlYXR1cmVzDQpGcm9tIGhlcmUsIHdlIGNhbiAgcnVuIGxpbmVhciByZWdyZXNzaW9uIHdpdGggQ3JpbWUgYmVpbmcgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBhbmQgZXZlcnl0aGluZyBlbHNlIGFzIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZS4gIEkgZGVjaWRlZCB0byBydW4gaXQgb24gdGhlIGVudGlyZSBzZXQgb2YgZmVhdHVyZXMgZm9yIHRoZSBmaXJzdCB0ZXN0LiAgSXQgcmV0dXJuZWQgdGhlIGZvbGxvd2luZyBjb2VmZmljaWVudHMgZm9yIGVhY2ggdmFyaWFibGUuDQoNCmBgYHtyfQ0KbW9kZWwgPC0gbG0oQ3JpbWV+LixjcmltZVRibCkNCmNvZWZNYXQgPC0gZGF0YS5mcmFtZShtb2RlbCRjb2VmZmljaWVudHMpDQprYWJsZShmb3JtYXQoY29lZk1hdCwgc2NpZW50aWZpYz1GKSkNCmBgYA0KDQojIyMgTGluZWFyIFJlZ3Jlc3Npb24gUGVyZm9ybWFuY2Ugb24gYWxsIGZlYXR1cmVzDQpUbyBhc3Nlc3MgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBtb2RlbCwgSSB1c2VkIHRoZSAkUl4yJCB2YWx1ZSBhbG9uZyB3aXRoIGEgcGxvdCBvZiB0aGUgcmVzaWR1YWxzIHRvIGRldGVybWluZSBob3cgd2VsbCB0aGUgbW9kZWwgZml0cyB0aGUgZGF0YS4gRmlyc3QsIHdlJ2xsIGxvb2sgYXQgdGhlIHIuc3F1YXJlZCB2YWx1ZSBhbmQgYWxzbyB0aGUgYWRqLnIuc3F1YXJlZCB2YWx1ZS4gIFNlYXJjaGluZyBvbmxpbmUgZm9yIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHR3bywgaXQgbG9va3MgbGlrZSB0aGUgci5zcXVhcmVkIHZhbHVlIGlzIHRoZSBzdGFuZGFyZCByLnNxdWFyZWQgdmFsdWUgd2Ugc2VlIGRlc2NyaWJlIGJ5IHRoZSBmb3JtdWxhIGJlbG93LiAgVGhpcyAkUl4yJCB2YWx1ZSB3aWxsIGFsd2F5cyBpbmNyZWFzZSB3aXRoIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIHlvdSBwcm92aWRlIHRvIHRoZSBsaW5lYXIgcmVncmVzc2lvbi4gIGFkai5yLnNxdWFyZWQgaXMgYWN0dWFsbHkgYSByLnNxdWFyZWQgdmFsdWUgdGhhdCBhZGp1c3RzIGZvciB0aGF0IGFkZGl0aW9uIGJ5IHRha2luZyBpbnRvIGFjY291bnQgdGhlIG51bWJlciBvZiB2YXJpYWJsZXMgdGhhdCBnZXRzIGFkZGVkIHRvIHRoZSBtb2RlbC4NCg0KJCRSXjI9MS1cZnJhY3tTU197ZXJyfX17U1Nfe3RvdH19JCQNCiRTU197ZXJyfSQ6IFN1bSBvZiBzcXVhcmVkIGRpc3RhbmNlcyBiZXR3ZWVuIHRoZSBhY3R1YWwgYW5kIHByZWRpY3RlZCBZIHZhbHVlcw0KJFNTX3t0b3R9JDogU3VtIG9mIHNxdWFyZWQgZGlzdGFuY2VzIGJldHdlZW4gdGhlIGFjdHVhbCBZIHZhbHVlcyBhbmQgdGhlaXIgbWVhbg0KDQpgYGB7cn0NCm1vZGVsU3VtIDwtIHN1bW1hcnkobW9kZWwpDQpwcmludChwYXN0ZTAoInIuc3F1YXJlZCB2YWx1ZSBpczogIixtb2RlbFN1bSRyLnNxdWFyZWQpKQ0KcHJpbnQocGFzdGUwKCJhZGouci5zcXVhcmVkIHZhbHVlIGlzOiAiLG1vZGVsU3VtJGFkai5yLnNxdWFyZWQpKQ0KYGBgDQpUaGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28gbWF5IGV4cGxhaW4gb3ZlcmZpdHRpbmcgaWYgdGhlIGRpZmZlcmVuY2UgaXMgbGFyZ2UuICBJIHRoaW5rIHRoZSBkaWZmZXJlbmNlIGlzIGRlY2VudGx5IGxhcmdlIHNvIGl0IGlzIHNob3dpbmcgdGhhdCB3ZSBtYXkgYmUgb3ZlcmZpdHRpbmcgb3VyIGRhdGEuICBOZXh0LCB3ZSdsbCB0YWtlIGEgbG9vayBhdCB0aGUgZ3JhcGggb2YgdGhlIHJlc2lkdWFscw0KDQpgYGB7cn0NCnFwbG90KHNlcV9hbG9uZyhtb2RlbCRyZXNpZHVhbHMpLG1vZGVsJHJlc2lkdWFscykrZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsY29sb3VyPSdyZWQnKStsYWJzKHRpdGxlPSJMaW5lYXIgUmVncmVzc2lvbiBSZXNpZHVhbCBQbG90Iix4PSJjaXR5Iix5PSJyZXNpZHVhbCIpDQpgYGANCldlIGNhbiBzZWUgYSBkZWNlbnQgYW1vdW50IG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YXBvaW50cyBzb21lIG9mIHdoaWNoIGhpdHRpbmcgdXAgdG8gNTEyIGNyaW1lcyB3aGVuIG91ciBtYXggZGF0YSB2YWx1ZSB3ZW50IHVwIHRvIDE5OTMuICBUaGVyZSBpcyBhIGRlY2VudCBhbW91bnQgb2YgdmFyaWF0aW9uIHNvIGxhdGVyIG9uIEknbSBnb2luZyB0byBzZWUgaWYgSSBjYW4gbmFycm93IHRoaXMgZG93biBhIGJpdCBtb3JlLiAgQnV0IGZpcnN0LCBJIHVzZWQgdGhpcyBtb2RlbCB0byBwcmVkaWN0IHRoZSBudW1iZXIgb2YgY3JpbWVzIGJhc2VkIG9uIHRoZSBob21ld29yayBkYXRhIHByb3ZpZGVkIHRvIHVzLg0KYGBge3J9DQp4IDwtIGRhdGEuZnJhbWUoImludCIgPSAxDQosIk0iID0gMTQuMA0KLCJTbyIgPSAwDQosIkVkIiA9IDEwLjANCiwiUG8xIiA9IDEyLjANCiwiUG8yIiA9IDE1LjUNCiwiTEYiID0gMC42NDANCiwiTS5GIiA9IDk0LjANCiwiUG9wIiA9IDE1MA0KLCJOVyIgPSAxLjENCiwiVTEiID0gMC4xMjANCiwiVTIiID0gMy42DQosIldlYWx0aCIgPSAzMjAwDQosIkluZXEiID0gMjAuMQ0KLCJQcm9iIiA9IDAuMDQNCiwiVGltZSIgPSAzOS4wKQ0KDQp4IDwtIGFzLm1hdHJpeCh4KQ0KY3JpbWUgPC0geCAlKiUgYXMubWF0cml4KG1vZGVsJGNvZWZmaWNpZW50cykNCnByaW50KHBhc3RlMCgiTnVtYmVyIG9mIGNyaW1lcyBpbiB0aGlzIGNpdHkgaXM6ICIsY3JpbWUpKQ0KYGBgDQoNCiMjIyBQaWNraW5nIEZlYXR1cmVzDQpPbmUgYXdlc29tZSB0aGluZyBhYm91dCBsbSBpbiByIGlzIHRoYXQgeW91IGNhbiBwdWxsIHRoZSB0IHZhbHVlIGFuZCB0aGUgUCB2YWx1ZSBkaXJlY3RseSBmcm9tIHRoZSBtb2RlbC4gIEknbSBnb2luZyB0byB1c2UgdGhlc2UgdmFsdWVzIHRvIHBpY2sgb3V0IG15IGZlYXR1cmVzLg0KDQpgYGB7cn0NCnRwdmFsdWUgPC0gZGF0YS5mcmFtZShtb2RlbFN1bSRjb2VmZmljaWVudHNbLDM6NF0pDQpjb2xuYW1lcyh0cHZhbHVlKSA8LSBjKCd0VmFsdWUnLCdwVmFsdWUnKQ0Ka2FibGUodHB2YWx1ZVtvcmRlcih0cHZhbHVlWywyXSksXSkNCmBgYA0KRnJvbSB0aGUgdGFibGUgYWJvdmUsIHlvdSBjYW4gc2VlIHRoYXQgdGhlIGZvbGxvd2luZyB2YWx1ZXMgYXJlIGdvb2QgcHJlZGljdG9ycyBiZWNhdXNlIHRoZWlyIHAtdmFsdWUgaXMgMC4wNSBvciBzbWFsbGVyLg0KDQoqIEluZXE6IEluY29tZSBpbmVxdWFsaXR5OiBwZXJjZW50YWdlIG9mIGZhbWlsaWVzIGVhcm5pbmcgYmVsb3cgaGFsZiB0aGUgbWVkaWFuIGluY29tZQ0KKiBFZDogbWVhbiB5ZWFycyBvZiBzY2hvb2xpbmcgb2YgdGhlIHBvcHVsYXRpb24gYWdlZCAyNSB5ZWFycyBvciBvdmVyDQoqIFByb2I6IHByb2JhYmlsaXR5IG9mIGltcHJpc29ubWVudDogcmF0aW8gb2YgbnVtYmVyIG9mIGNvbW1pdG1lbnRzIHRvIG51bWJlciBvZiBvZmZlbnNlcw0KKiBNOiBwZXJjZW50YWdlIG9mIG1hbGVzIGFnZWQgMTQtMjQgaW4gdG90YWwgc3RhdGUgcG9wdWxhdGlvbg0KKiBVMjogdW5lbXBsb3ltZW50IHJhdGUgb2YgdXJiYW4gbWFsZXMgMzUtMzkNCiogUG8xOiBwZXIgY2FwaXRhIGV4cGVuZGl0dXJlIG9uIHBvbGljZSBwcm90ZWN0aW9uIGluIDE5NjANCg0KQmVsb3cgaXMgYSBsaXN0IG9mIHZhcmlhYmxlcyB0aGF0IGFyZSBiYWQgcHJlZGljdG9ycy4NCg0KKiBTbzogaW5kaWNhdG9yIHZhcmlhYmxlIGZvciBhIHNvdXRoZXJuIHN0YXRlDQoqIExGOiBsYWJvdXIgZm9yY2UgcGFydGljaXBhdGlvbiByYXRlIG9mIGNpdmlsaWFuIHVyYmFuIG1hbGVzIGluIHRoZSBhZ2UtZ3JvdXAgMTQtMjQNCiogVGltZTogYXZlcmFnZSB0aW1lIGluIG1vbnRocyBzZXJ2ZWQgYnkgb2ZmZW5kZXJzIGluIHN0YXRlIHByaXNvbnMgYmVmb3JlIHRoZWlyIGZpcnN0IHJlbGVhc2UNCiogUG9wOiBzdGF0ZSBwb3B1bGF0aW9uIGluIDE5NjAgaW4gaHVuZHJlZCB0aG91c2FuZHMNCiogTlc6IHBlcmNlbnRhZ2Ugb2Ygbm9ud2hpdGVzIGluIHRoZSBwb3B1bGF0aW9uDQoqIE0uRjogbnVtYmVyIG9mIG1hbGVzIHBlciAxMDAgZmVtYWxlcw0KKiBXZWFsdGg6IHdlYWx0aDogbWVkaWFuIHZhbHVlIG9mIHRyYW5zZmVyYWJsZSBhc3NldHMgb3IgZmFtaWx5IGluY29tZQ0KKiBQbzI6IHBlciBjYXBpdGEgZXhwZW5kaXR1cmUgb24gcG9saWNlIHByb3RlY3Rpb24gaW4gMTk1OQ0KKiBVMTp1bmVtcGxveW1lbnQgcmF0ZSBvZiB1cmJhbiBtYWxlcyAxNC0yNA0KDQpMZXQncyBhbHNvIHRha2UgYSBsb29rIGF0IHRoZSBjb3JyZWxhdGlvbiBwbG90IGJldHdlZW4gYWxsIG9mIHRoZSB2YXJpYWJsZSB0byBmaW5kIG91dCB3aGljaCBvbmVzIGFyZSBoaWdobHkgY29ycmVsYXRlZC4NCg0KYGBge3IsZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDh9DQpsaWJyYXJ5KGNvcnJwbG90KQ0KTSA8LSBjb3IoY3JpbWVUYmwpDQpjb3JycGxvdC5taXhlZChNLHRpdGxlPSJDb3JyZWxhdGlvbiBvZiBGZWF0dXJlcyIsbWFyPWMoMCwwLDEsMCkpDQpgYGANCkZyb20gdGhpcyBmaWd1cmUsIHdlIGNhbiBzZWUgdGhlIGZvbGxvd2luZyBmZWF0dXJlcyBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQgc28gd2Ugc2hvdWxkIHByb2JhYmx5IG9ubHkgaW5jbHVkZSBvbmUgb2YgdGhlbSBpbiB0aGUgc2VsZWN0ZWQgZmVhdHVyZXMuDQoqIFdlYWx0aCA6OiBJbmVxDQoqIEluZXEgOjogRWQNCiogU28gOjogRWQNCiogV2VhbHRoIDo6IFBvMSA6OiBQbzIgDQoNCiMjIyBMaW5lYXIgUmVncmVzc2lvbiBvbiBTZWxlY3RlZCBGZWF0dXJlcw0KSSBhY3R1YWxseSB0ZXN0ZWQgdGhpcyBtb2RlbCBieSByZW1vdmluZyBlaXRoZXIgSW5lcSBhbmQgRWQgYW5kIGNoZWNrZWQgdGhlIGFkanVzdGVkIHItc3F1YXJlZCB2YWx1ZSBhZm91bmQgdGhhdCByZW1vdmluZyBlaXRoZXIgb25lIHNpZ25pZmljYW50bHkgZHJvcHBlZCB0aGUgdmFsdWUuICBJdCBzZWVtcyB0aGF0IHRoZXkgbWF5IGJlIGhlbHBpbmcgdGhlIG1vZGVsIGluIGRpZmZlcmVudCB3YXlzLg0KDQpgYGB7cn0NCmZlYXRUb1VzZSA8LSBjKCdJbmVxJywnRWQnLCdQcm9iJywnTScsJ1UyJywnUG8xJywnQ3JpbWUnKQ0Kc21hbGxDcmltZVRibCA8LSBjcmltZVRibFtmZWF0VG9Vc2VdDQptb2RlbDIgPC0gbG0oQ3JpbWV+LixzbWFsbENyaW1lVGJsKQ0KbW9kZWwyU3VtIDwtIHN1bW1hcnkobW9kZWwyKQ0KcHJpbnQocGFzdGUwKCJyLnNxdWFyZWQgdmFsdWUgaXM6ICIsbW9kZWwyU3VtJHIuc3F1YXJlZCkpDQpwcmludChwYXN0ZTAoImFkai5yLnNxdWFyZWQgdmFsdWUgaXM6ICIsbW9kZWwyU3VtJGFkai5yLnNxdWFyZWQpKQ0KYGBgDQpXZSdyZSBub3Qgc2VlaW5nIGFzIGxhcmdlIG9mIGEgZGlmZmVyZW5jZSBhcyB3ZSBzYXcgYmVmb3JlLg0KDQpgYGB7cn0NCnFwbG90KHNlcV9hbG9uZyhtb2RlbDJTdW0kcmVzaWR1YWxzKSxtb2RlbDJTdW0kcmVzaWR1YWxzKStnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCxjb2xvdXI9J3JlZCcpK2xhYnModGl0bGU9IkxpbmVhciBSZWdyZXNzaW9uIFJlc2lkdWFsIFBsb3QiLHg9ImNpdHkiLHk9InJlc2lkdWFsIikNCmBgYA0KQmVjYXVzZSB0aGUgc2NhbGVzIGFyZSBzbGlnaHRseSBhZGp1c3RlZCwgSSBjYW4ndCBzYXkgZm9yIHN1cmUgdGhhdCB0aGUgc3ByZWFkIGhhcyBjaGFuZ2VkLCBzbyBpdCB3b3VsZCBiZSBpbmNvbmNsdXNpdmUgZnJvbSB0aGlzIGNoYXJ0LiAgVGhlIGJlc3Qgd2F5IHRvIGRldGVybWluZSB0aGUgZGlmZmVyZW5jZSBpcyB0byBydW4gY3Jvc3MgdmFsaWRhdGlvbiBvbiBib3RoIG1vZGVscy4NCg0KIyMjIENyb3NzIFZhbGlkYXRpb24NCldlIGNhbiBwZXJmb3JtIGNyb3NzIHZhbGlkYXRpb24gb24gYm90aCBtb2RlbHMuICBGaXJzdCBvbmUgd2hlcmUgd2UgdXNlZCBhbGwgdGhlIGZlYXR1cmVzIGFuZCB0aGUgc2Vjb25kIG9uZSB3aGVyZSB3ZSBvbmx5IHVzZWQgYSBzZXJpZXMgb2YgZmVhdHVyZXMgd2UgaGFuZCBwaWNrZWQuDQoNCmBgYHtyfQ0KbGlicmFyeShEQUFHKQ0KY3JpbWVkZiA8LSBkYXRhLmZyYW1lKGNyaW1lVGJsKQ0KbW9kQWxsIDwtIENWbG0oY3JpbWVkZixmb3JtLmxtPWZvcm11bGEoQ3JpbWV+LiksbT01LHNlZWQ9NDQscHJpbnRpdD1GQUxTRSxwbG90aXQ9IlJlc2lkdWFsIixtYWluPSJBbGwgZmVhdHVyZXMiKQ0KbW9kU2VsIDwtIENWbG0oc21hbGxDcmltZVRibCxmb3JtLmxtPWZvcm11bGEoQ3JpbWV+LiksbT01LHNlZWQ9NDQscHJpbnRpdD1GQUxTRSxwbG90aXQ9IlJlc2lkdWFsIixtYWluPSJTZWxlY3QgZmVhdHVyZXMiKQ0KYGBgDQpGcm9tIHBsb3R0aW5nIG91dCB0aGUgcmVzaWR1YWxzIGl0IGxvb2tzIGxpa2UgbW9kZWwyKHNlbGVjdCBmZWF0dXJlcykgaXMgbW9yZSBjb25zaXN0ZW50IGFuZCBmb3JtIGEgdGlnaHRlciBncm91cGluZyB0aGFuIG1vZGVsMShhbGwgZmVhdHVyZXMpLiAgU28gd2UgZ28gYWhlYWQgYW5kIHRha2UgYSBsb29rIGF0IHRoZSAkUl4yJCB2YWx1ZSBhbmQgYWxzbyB0aGUgbWVhbiBzcXVhcmVkIGVycm9yIG9mIGJvdGggcGxvdHMuDQoNCmBgYHtyfQ0KU1N0b3QgPC0gc3VtKChtb2RBbGwkQ3JpbWUtbWVhbihtb2RBbGwkQ3JpbWUpKV4yKQ0KU1NyZXNfbW9kMSA8LSBzdW0oKG1vZEFsbCRQcmVkaWN0ZWQtbW9kQWxsJENyaW1lKV4yKQ0KU1NyZXNfbW9kMiA8LSBzdW0oKG1vZFNlbCRQcmVkaWN0ZWQtbW9kU2VsJENyaW1lKV4yKQ0KDQojQ2FsY3VsYXRlIFJeMg0KbW9kMVIyIDwtIDEtU1NyZXNfbW9kMS9TU3RvdA0KbW9kMlIyIDwtIDEtU1NyZXNfbW9kMi9TU3RvdA0KDQpwcmludChwYXN0ZTAoJ2FsbCBmZWF0dXJlcyBSXjI6ICcsbW9kMVIyKSkNCnByaW50KHBhc3RlMCgnc2VsZWN0IGZlYXR1cmVzIFJeMjogJyxtb2QyUjIpKQ0KcHJpbnQocGFzdGUwKCdhbGwgZmVhdHVyZXMgbWVhbiBzcXVhcmU6ICcsYXR0cihtb2RBbGwsIm1zIikpKQ0KcHJpbnQocGFzdGUwKCdzZWxlY3QgZmVhdHVyZXMgbWVhbiBzcXVhcmU6ICcsYXR0cihtb2RTZWwsIm1zIikpKQ0KDQpgYGANCldoZW4gd2UgbG9vayBhdCB0aGUgY3Jvc3MgdmFsaWRhdGlvbiBkYXRhLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIG1vZGVsIHdpdGggYWxsIGZlYXR1cmVzIGFjdHVhbGx5IGRvZXMgYmV0dGVyIHRoYW4gdGhlIHNlbGVjdGVkIGZlYXR1cmVzIG1vZGVsLiAgVGhpcyBhY3R1YWxseSBjb21lcyBhcyBhIHN1cnByaXNlIHRvIG1lIHNpbmNlIHRoZSBhZGp1c3RlZCByIHNxdWFyZWQgdmFsdWUgZGlzcGxheWVkIGJldHRlciBwZXJmb3JtYW5jZSBmb3IgdGhlIHNlbGVjdCBmZWF0dXJlcy4gIEJ1dCB0aGlzIGlzIHRoZSByZWFzb24gd2h5IHdlIHBlcmZvcm0gY3Jvc3MgdmFsaWRhdGlvbiB0byBjb25maXJtLg0KDQoNCg0KDQoNCg==